from odoo import fields, models, api
from odoo.exceptions import UserError
from functools import wraps


def exception_handler(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        try:
            return func(self, *args, **kwargs)
        except UserError as e:
            # self.env.user.notify_danger(message='您的操作异常，如有疑问可以联系管理员', title='User Error')
            model = self.env['ir.model'].search([('model', '=', self._name)], limit=1)
            self.env['res.error.log'].create({'model_id': model.id if model else False,
                                              'record_id': self.id if hasattr(self, 'id') else False,
                                              'function': func.__name__,
                                              'type': 'user',
                                              'desc': str(e)})
        except Exception as e:
            # self.env.user.notify_danger(message='已创建错误日志，请联系管理员', title='Error Tags')
            model = self.env['ir.model'].search([('model', '=', self._name)], limit=1)
            self.env['res.error.log'].create({'model_id': model.id if model else False,
                                              'record_id': self.id if hasattr(self, 'id') else False,
                                              'function': func.__name__,
                                              'type': 'try',
                                              'desc': str(e)})

    return wrapper


class RESErrorLog(models.Model):
    _name = 'res.error.log'
    _description = 'RES错误日志'
    _sql_constraints = [('unique_name', 'UNIQUE(code)', '名称必须唯一')]
    _rec_name = 'code'

    code = fields.Char(string='编号', default='RES_EL/*****', copy=False, index=True)

    model_id = fields.Many2one(string='关联模型', comodel_name='ir.model')
    record_id = fields.Integer(string='记录ID')

    function = fields.Char(string='函数名')

    user_id = fields.Many2one(string='操作人', comodel_name='res.users', default=lambda self: self.env.user)

    type = fields.Selection(string='类型', selection=[('model', '模型函数'),  # depends/onchange/create/write
                                                    ('button', '按钮函数'),  # action控制
                                                    ('function', '功能函数'),  # 外部公共函数
                                                    ('arithmetic', '算法函数'),  # 公共算法函数
                                                    ('orm', 'ORM'),
                                                    ('sql', 'SQL'),
                                                    ('user', '用户操作'),
                                                    ('try', '保护机制触发'),
                                                    ('connector', '接口'),
                                                    ('other', '其他')], default='try')

    desc = fields.Text(string='说明')

    @api.model_create_multi
    def create(self, vals):
        for val in vals:
            vals['code'] = self.env['ir.sequence'].sudo().next_by_code('res.error.log') or '/'
        return super(RESErrorLog, self).create(vals)

    def action_to_source_order(self):
        """ 上查获取视图 """
        if self.model_id and self.record_id >= 0:
            res_model = self.model_id.model  # 获取表名
            rec_act, rec_module = self.sql_get_act_window_id(res_model)
            action_name = str(rec_module) + "." + str(rec_act)
            action = self.env.ref(action_name)  # 动作视图
            # print(action.read())
            result = action.read()[0]
            result['type'] = 'ir.actions.act_window'
            result['view_type'] = 'form'
            result['view_mode'] = 'form'
            result['target'] = 'new'
            result['flags'] = {'initial_mode': 'view', 'action_buttons': False}
            view_name = str(self._get_form_view(res_model))
            res = self.env.ref(view_name)  # form视图
            result['views'] = [(res.id, 'form')]
            result['res_id'] = self.record_id
            return result
        else:
            raise UserError("没有来源，无法打开视图")
            # self.env.user.notify_warning(message='没有来源，无法打开视图', title='错误提醒')

    def sql_get_act_window_id(self, model_name):
        """
        通过模型名获取窗口动作ID
        :param model_name: 模型名
        :return: 视图名，模块名
        """
        try:
            # 1.通过模型名获取窗口动作的ID
            sql_id = "select id from ir_act_window " \
                     "where res_model ='{0}' ".format(model_name)
            self.env.cr.execute(sql_id)  # 执行sql语句
            rec_id = self.env.cr.dictfetchall()  # 获取sql的查询结果
            # 2.通过ID获取当前模型ir.action.act_window的名称
            sql_act = "select id,name,module from ir_model_data " \
                      "where res_id = '{0}' " \
                      "and model = 'ir.actions.act_window' " \
                      "order by id DESC".format(rec_id[0]['id'])  # 倒序排序，取第一条名称
            self.env.cr.execute(sql_act)  # 执行sql语句
            rec_act = self.env.cr.dictfetchall()  # 获取sql的查询结果
            return rec_act[0]['name'], rec_act[0]['module']
        except Exception as e:
            raise UserError("SQL_Error_0118:获取窗口动作ID,请及时联系管理员")

    def _get_form_view(self, res_model):
        """获取对应模型的视图"""
        view_list = self.env['ir.ui.view'].search([('model', '=', res_model)])
        view_pos = ''
        for view in view_list:
            if view.name.rfind('form') != -1:  # 如果可以模糊查询到form
                if view.name.rfind('line') == -1:  # 如果不能模糊查询到line
                    view_pos = view.xml_id
        if view_pos != '':
            return view_pos
        else:
            raise UserError("Search_Error_0401:获取对应模型的视图,请及时联系管理员")
